package pso;

import pso.implementation.PSO;
import pso.particle.ParticleNeighborhood;
import pso.particle.SwarmInitializer;
import pso.position.DefaultPositionUpdateStrategy;
import pso.velocity.ClassicVelocityUpdateStrategy;
import pso.velocity.ConstantInertiaWeightVelocityUpdateStrategy;
import pso.velocity.ConstrictionFactorVelocityUpdateStrategy;
import pso.velocity.LinearlyDecreasingInertiaWeightVelocityUpdateStrategy;
import pso.velocity.RandomInertiaWeightVelocityUpdateStrategy;
import evolution.problem.OptimizationProblem;
import evolution.problem.ProblemConstraints;
import evolution.stop.IterationStopCondition;

public class PSOAlgorithmFactory {

	public static PSOAlgorithm createClassicPSO(OptimizationProblem problem,
			int iterations, int swarmSize) {
		return createClassicPSO(problem, iterations, swarmSize, 2.05, 2.05);
	}

	public static PSOAlgorithm createClassicPSO(OptimizationProblem problem,
			int iterations, int swarmSize, double cognitiveFactor,
			double socialFactor) {
		PSOAlgorithm algorithm = buildGenericPSO(problem, iterations,
				swarmSize, cognitiveFactor, socialFactor);

		ClassicVelocityUpdateStrategy velocityUpdateStrategy = new ClassicVelocityUpdateStrategy();
		velocityUpdateStrategy.setCognitiveFactor(cognitiveFactor);
		velocityUpdateStrategy.setSocialFactor(socialFactor);
		velocityUpdateStrategy.configureMaximumVelocity(problem
				.getConstraints());
		algorithm.setVelocityUpdateStrategy(velocityUpdateStrategy);

		String desc = "Klasyczne PSO (c1=" + cognitiveFactor + ", c2="
				+ socialFactor + ")";
		PSODisplayData data = new PSODisplayData(PSO.CLASSIC, desc);
		AlgorithmsDataList.getInstance().addDisplayData(algorithm, data);

		return algorithm;
	}

	public static PSOAlgorithm createRandomInertiaWeightPSO(
			OptimizationProblem problem, int iterations, int swarmSize) {
		PSOAlgorithm algorithm = createRandomInertiaWeightPSO(problem,
				iterations, swarmSize, 1.494, 1.494);
		return algorithm;
	}

	public static PSOAlgorithm createRandomInertiaWeightPSO(
			OptimizationProblem problem, int iterations, int swarmSize,
			double cognitiveFactor, double socialFactor) {
		PSOAlgorithm algorithm = buildGenericPSO(problem, iterations,
				swarmSize, cognitiveFactor, socialFactor);

		RandomInertiaWeightVelocityUpdateStrategy strategy = new RandomInertiaWeightVelocityUpdateStrategy();
		strategy.configureMaximumVelocity(problem.getConstraints());
		algorithm.setVelocityUpdateStrategy(strategy);

		String desc = "PSO z losową inercją (c1=" + cognitiveFactor + ", c2="
				+ socialFactor + ")";
		PSODisplayData data = new PSODisplayData(PSO.RANDOM_INERTIA, desc);
		AlgorithmsDataList.getInstance().addDisplayData(algorithm, data);

		return algorithm;
	}

	public static PSOAlgorithm createConstantInertiaWeightPSO(
			OptimizationProblem problem, int iterations, int swarmSize,
			double cognitiveFactor, double socialFactor, double inertiaWeight) {
		PSOAlgorithm algorithm = buildGenericPSO(problem, iterations,
				swarmSize, cognitiveFactor, socialFactor);

		ConstantInertiaWeightVelocityUpdateStrategy strategy = new ConstantInertiaWeightVelocityUpdateStrategy(
				inertiaWeight);
		strategy.configureMaximumVelocity(problem.getConstraints());
		algorithm.setVelocityUpdateStrategy(strategy);

		String desc = "PSO ze stałą inercją (c1=" + cognitiveFactor + ", c2="
				+ socialFactor + ", ω=" + inertiaWeight + ")";
		PSODisplayData data = new PSODisplayData(PSO.CONSTANT_INERTIA, desc);
		AlgorithmsDataList.getInstance().addDisplayData(algorithm, data);

		return algorithm;
	}

	public static PSOAlgorithm createDecreasingInertiaWeightPSO(
			OptimizationProblem problem, int iterations, int swarmSize,
			double cognitiveFactor, double socialFactor,
			double beginInertiaWeight, double endInertiaWeight) {
		PSOAlgorithm algorithm = buildGenericPSO(problem, iterations,
				swarmSize, cognitiveFactor, socialFactor);
		LinearlyDecreasingInertiaWeightVelocityUpdateStrategy strategy = new LinearlyDecreasingInertiaWeightVelocityUpdateStrategy(
				algorithm, beginInertiaWeight, endInertiaWeight);
		strategy.configureMaximumVelocity(problem.getConstraints());
		algorithm.setVelocityUpdateStrategy(strategy);

		String desc = "PSO z malejącą inercją (c1=" + cognitiveFactor + ", c2="
				+ socialFactor + ", ω=" + beginInertiaWeight + ":"
				+ endInertiaWeight + ")";
		PSODisplayData data = new PSODisplayData(PSO.DECREASING_INERTIA, desc);
		AlgorithmsDataList.getInstance().addDisplayData(algorithm, data);

		return algorithm;
	}

	public static PSOAlgorithm createConstrictedPSO(
			OptimizationProblem problem, int iterations, int swarmSize,
			double cognitiveFactor, double socialFactor) {
		PSOAlgorithm algorithm = buildGenericPSO(problem, iterations,
				swarmSize, cognitiveFactor, socialFactor);

		ConstrictionFactorVelocityUpdateStrategy strategy = new ConstrictionFactorVelocityUpdateStrategy();
		strategy.configureMaximumVelocity(problem.getConstraints());
		algorithm.setVelocityUpdateStrategy(strategy);

		String desc = "PSO ze wsp. ścisku (c1=" + cognitiveFactor + ", c2="
				+ socialFactor + ")";
		PSODisplayData data = new PSODisplayData(PSO.CONSTRICTION, desc);
		AlgorithmsDataList.getInstance().addDisplayData(algorithm, data);

		return algorithm;
	}

	private static PSOAlgorithm buildGenericPSO(OptimizationProblem problem,
			int iterations, int swarmSize, double cognitiveFactor,
			double socialFactor) {
		PSOAlgorithmImpl algorithm = new PSOAlgorithmImpl();

		algorithm.setOptimizationProblem(problem);

		ProblemConstraints constraints = problem.getConstraints();
		DefaultPositionUpdateStrategy positionUpdateStrategy = new DefaultPositionUpdateStrategy(
				constraints);
		algorithm.setPositionUpdateStrategy(positionUpdateStrategy);

		algorithm.setStopCondition(new IterationStopCondition(iterations));

		ParticleNeighborhood swarm = SwarmInitializer.initializeSwarm(problem,
				swarmSize);
		algorithm.setSwarm(swarm.getNeighbours());

		return algorithm;
	}
}
